//------------------------------------------------------------------------------
//SFWK Framework
//Copyright (C) 2011 SQLI

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.StaticFactory;
using System.Reflection;
using System.ServiceModel.MsmqIntegration;
using SFWK.Core.Unity.DynamicProxy;
using System.ServiceModel.Description;

using System.ServiceModel.Dispatcher;
using System.Xml;
using System.Threading;
using System.Web.Security;
using System.Linq;
using SFWK.Core.Cryptography;
using SFWK.Core.Service;

namespace SFWK.Core.Unity
{
   
    /// <summary>
    /// Service proxy factory providing local or WCF remote services access
    /// </summary>
    public static class ServiceProvider
    {
        #region Enum
        /// <summary>
        /// List öf available WCF communication layer
        /// </summary>
        enum EBinding
        {
            BasicHttpBinding,
            WSHttpBinding,
            WSDualHttpBinding,
            WSFederationHttpBinding,
            NetTcpBinding,
            NetPeerTcpBinding,
            NetNamedPipeBinding,
            NetMsmqBinding,
            MsmqIntegrationBinding
        }
        #endregion

        #region Properties
        /// <summary>
        /// Unity conitainer
        /// </summary>
        private static IUnityContainer _container = null;

        /// <summary>
        /// Contains list of registered interfaces
        /// </summary>
        private static System.Collections.Hashtable _registeredTypes;
        #endregion Properties

        #region Public methods

        /* Web.Config sample
        <UnityConfiguration>
        <services defautlServiceLocation="http://localhost:8731/Design_Time_Addresses/">
            <clear />
            <add type="CMA.Common.ILogger, CMA.Common" localImplementation="CMA.Service.ServiceClient, CMA.Service.Implementation" />
            <add type="IParameterInspector, System.ServiceModel.Dispatcher"  localImplementation="CMA.Service.Inspector.InspectorParameter, CMA.Service.Inspector"/>			
            <add type="CMA.Service.IServiceClient, CMA.Service" localImplementation="CMA.Service.ImplementationSpecial"/>
            <add type="CMA.Service.*, CMA.Service" localImplementation="CMA.Service.Implementation"/>
            <add type="CMA.Service2.IServiceCompte, CMA.Service"  />
            <add type="CMA.Service.IServiceVirement, CMA.Service" endpointAddress="http://localhost:8731/Design_Time_Addresses/CMA.Service/ServiceVirement/"
             endpointBinding="wsHttpBinding" endpointContract="CMA.Service.IServiceVirement" />
        </services>
    </UnityConfiguration>*/


        /// <summary>
        /// Implement service instance depending on config file
        /// </summary>
        /// <typeparam name="T">Service instance type</typeparam>
        /// <returns>Service instance</returns>
        public static T GetService<T>()
        {
            if (_container == null)
            {
                _container = new UnityContainer();
                foreach(ServiceConfigElement srvConfig in Configuration.UnityConfigurationSection.Services)
                {
                    if (!string.IsNullOrEmpty(srvConfig.Type))
                    {
                        if (!string.IsNullOrEmpty(srvConfig.LocalImplementation))
                        {
                            RegisterLocalServices(srvConfig);
                        }
                        else
                        {
                            RegisterRemoteServices(srvConfig, Configuration.UnityConfigurationSection);
                        }
                    }
                }
            }
            return _container.Resolve<T>();
        }

        #endregion

        #region private registration method
        /// <summary>
        /// Register a local class
        /// </summary>
        /// <param name="serviceConfig">Data from config file used to register local service</param>
        private static void RegisterLocalServices(ServiceConfigElement serviceConfig)
        {
            string interfaceNameFrom = "";
            Assembly assemblyFrom = null;
            //Treat Type From
            _getInterfaceInfo(serviceConfig.Type, ref assemblyFrom, ref interfaceNameFrom);
            
            //Treat Implementation
            string assemblyNameTo = "";
            string objectNameTo = "";
            if (serviceConfig.LocalImplementation.Contains(","))
            {
                string[] objTo = serviceConfig.LocalImplementation.Split(',');
                assemblyNameTo = objTo[1].Trim();
                objectNameTo = objTo[0].Trim();
            }
            else
            {
                assemblyNameTo = serviceConfig.LocalImplementation;
            }
            Assembly assemblyTo = System.Reflection.Assembly.Load(assemblyNameTo);


            if (interfaceNameFrom.EndsWith(".*") || interfaceNameFrom == "")
            {
                //Loads All interfaces of the from namespaces in the implementation namespace
                foreach (Type tinterface in assemblyFrom.GetTypes())
                {
                    if (tinterface.GetCustomAttributes(typeof(ServiceContractAttribute), true).Length > 0 &&
                        !tinterface.Name.EndsWith("Base")) // On ne charge pas les classes de base des services
                    {
                        //Type timplementation = assemblyTo.GetType(tinterface.Namespace + "." + tinterface.Name.Substring(1));
                        Type timplementation = assemblyTo.GetType(assemblyNameTo + "." + tinterface.Name.Substring(1));

                        if (!_isInterfaceRegistered(tinterface.Namespace + "." + tinterface.Name))
                        {
                            if (serviceConfig.UseDataContext)
                            {
                                _registerLocalWithDynamicProxy(tinterface, timplementation);
                            }
                            else
                            {
                                _registerInterface(tinterface.Namespace + "." + tinterface.Name);
                                _container.RegisterType(tinterface, timplementation);
                            }
                        }
                    }
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(objectNameTo))
                {
                    //Load one interface in the requested object implementation
                    if (!_isInterfaceRegistered(interfaceNameFrom))
                    {
                        if (serviceConfig.UseDataContext)
                        {
                            _registerLocalWithDynamicProxy(assemblyFrom.GetType(interfaceNameFrom), _getType(assemblyNameTo, objectNameTo));
                        }
                        else
                        {
                            _registerInterface(interfaceNameFrom);
                            _container.RegisterType(assemblyFrom.GetType(interfaceNameFrom), _getType(assemblyNameTo, objectNameTo));
                        }
                    }
                }
                else
                {
                    //Load one interface in the object of the requested implementation namespace
                    objectNameTo = interfaceNameFrom.Substring(interfaceNameFrom.LastIndexOf(".I"));
                    if (!_isInterfaceRegistered(interfaceNameFrom))
                    {
                        if (serviceConfig.UseDataContext)
                        {
                            _registerLocalWithDynamicProxy(assemblyFrom.GetType(interfaceNameFrom), _getType(assemblyNameTo, objectNameTo));
                        }
                        else
                        {
                            _registerInterface(interfaceNameFrom);
                            _container.RegisterType(assemblyFrom.GetType(interfaceNameFrom), _getType(assemblyNameTo, objectNameTo));
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Registers a remote service
        /// </summary>
        /// <param name="serviceConfig">Data from config file to specify endpoint address, binding,...</param>
        /// <param name="defaultServiceLocation">Default uri of the services</param>
        private static void RegisterRemoteServices(ServiceConfigElement serviceConfig, UnityConfigurationSection globalConfig)// string defaultServiceLocation, bool iisHosted, long maxReceivedMessageSize, int operationTimeOut)
        {
            string interfaceNameFrom = "";
            Assembly assemblyFrom = null;
            //Treat Type From
            _getInterfaceInfo(serviceConfig.Type, ref assemblyFrom, ref interfaceNameFrom);
            

            //Treat Implementation            
            _container.AddNewExtension<StaticFactoryExtension>();
            IStaticFactoryConfiguration cff = _container.Configure<IStaticFactoryConfiguration>();
            if (interfaceNameFrom != "")
            {
                if (!_isInterfaceRegistered(interfaceNameFrom))
                {
                    _registerInterface(interfaceNameFrom);

                    Type interfaceFrom = assemblyFrom.GetType(interfaceNameFrom);
                    Type cfcg = typeof(WcfChannelFactory<>);
                    Type cfcs = cfcg.MakeGenericType(new Type[] { interfaceFrom });

                    ICreateService instance = (ICreateService)Activator.CreateInstance(cfcs, serviceConfig.EndpointBinding, serviceConfig.EndpointAddress, serviceConfig.EndpointContract, globalConfig);

                    MethodInfo mig = cff.GetType().GetMethod("RegisterFactory", new Type[] { typeof(FactoryDelegate) });
                    MethodInfo mis = mig.MakeGenericMethod(new System.Type[] { interfaceFrom });
                    mis.Invoke(cff, new object[] { new FactoryDelegate(instance.CreateService) });
                }
            }
            else 
            {
                foreach (Type serviceType in assemblyFrom.GetTypes())
                {
                    if (serviceType.GetCustomAttributes(typeof(ServiceContractAttribute), true).Length > 0)
                    {
                        if (!_isInterfaceRegistered(serviceType.Namespace + "." + serviceType.Name))
                        {
                            _registerInterface(serviceType.Namespace + "." + serviceType.Name);
                            Type cfcg = typeof(WcfChannelFactory<>);
                            Type cfcs = cfcg.MakeGenericType(new Type[] { serviceType });

                            ICreateService instance = (ICreateService)Activator.CreateInstance(cfcs, serviceConfig.EndpointBinding, serviceConfig.EndpointAddress, serviceConfig.EndpointContract, globalConfig);

                            MethodInfo mig = cff.GetType().GetMethod("RegisterFactory", new Type[] { typeof(FactoryDelegate) });
                            MethodInfo mis = mig.MakeGenericMethod(new System.Type[] { serviceType });
                            mis.Invoke(cff, new object[] { new FactoryDelegate(instance.CreateService) });
                        }
                    }
                }
            }
        }
        #endregion



        #region interface ICreateDynamicProxy
        /// <summary>
        /// Dynamic Proxy factory interface
        /// </summary>
        internal interface ICreateDynamicProxy
        {
            object CreateProxy(IUnityContainer c);
        }
        #endregion

        #region class DynamicProxyFactory<T>
        /// <summary>
        /// WCF Channel factory class
        /// </summary>
        /// <typeparam name="T">Type of service</typeparam>
        internal class UnityDynamicProxyFactory : ICreateDynamicProxy
        {
            #region Properties
            private Type _type;
            #endregion
            public UnityDynamicProxyFactory(Type type) 
            {
                _type = type;
            }
            #region ICreateDynamicProxy Members

            public object CreateProxy(IUnityContainer c)
            {
                return DynamicProxyFactory.Instance.CreateProxy(Activator.CreateInstance(_type), new InvocationDelegate(InvocationHandler));
            }

            #endregion

            
        }
        #endregion

        #region interface ICreateService
        /// <summary>
        /// WCF Channel factory interface
        /// </summary>
        internal interface ICreateService
        {
            object CreateService(IUnityContainer c);
        }
        #endregion

        #region class WcfChannelFactory<T>
        /// <summary>
        /// WCF Channel factory class
        /// </summary>
        /// <typeparam name="T">Type of service</typeparam>
        internal class WcfChannelFactory<T> : ICreateService, IDisposable
        {
            #region properties
            string _endPointBinding;
            string _endPointAddress;
            string _endPointContract;
            string _defaultServiceLocation;
            bool _iisHosted;
            long _maxReceivedMessageSize;
            int _operationTimeout;
            int _maxItemsInObjectGraph;

            private ChannelFactory<T> _channelFactory = null;

            #endregion

            #region Constructor
            /// <summary>
            /// Constructor with endpoint data
            /// </summary>
            /// <param name="endPointBinding"></param>
            /// <param name="endPointAddress"></param>
            /// <param name="endPointContract"></param>
            /// <param name="defaultServiceLocation"></param>
            public WcfChannelFactory(string endPointBinding, string endPointAddress, string endPointContract, UnityConfigurationSection globalConfig) : base()
            {
                // set member parameters.
                _endPointBinding = endPointBinding;
                _endPointAddress = endPointAddress;
                _endPointContract = endPointContract;
                _defaultServiceLocation = globalConfig.DefautlServiceLocation;
                _iisHosted = globalConfig.IISHosted;
                _maxReceivedMessageSize = globalConfig.MaxReceivedMessageSize;
                _operationTimeout = globalConfig.OperationTimeout;
                _maxItemsInObjectGraph = globalConfig.MaxItemsInObjectGraph;
            }
            #endregion

            private ChannelFactory<T> Configure(ChannelFactory<T> cf)
            {
                if (!cf.Endpoint.Behaviors.Contains(typeof(ServiceBehavior)))
                    cf.Endpoint.Behaviors.Add(new ServiceBehavior());

                foreach (OperationDescription op in cf.Endpoint.Contract.Operations)
                {
                    DataContractSerializerOperationBehavior dataContractBehavior = op.Behaviors[typeof(DataContractSerializerOperationBehavior)] as DataContractSerializerOperationBehavior;
                    if (dataContractBehavior != null)
                        dataContractBehavior.MaxItemsInObjectGraph = int.MaxValue; //_maxItemsInObjectGraph
                }

                return cf;
            }

            #region ICreateService Members
            /// <summary>
            /// Create a channel. Default is WSHttpBinding used
            /// </summary>
            /// <param name="c"></param>
            /// <returns></returns>
            object ICreateService.CreateService(IUnityContainer c)
            {
                T channel;

                if (string.IsNullOrEmpty(_endPointContract))
                    _endPointContract = typeof(T).Name;

                if (!string.IsNullOrEmpty(_endPointAddress))
                {
                    string address = _endPointAddress;
                    if (!address.EndsWith("/"))
                        address += "/";

                    address += _endPointContract.Substring(1) + ".svc";


                    if (!string.IsNullOrEmpty(_endPointBinding))
                    {
                        _channelFactory = new ChannelFactory<T>(_getBinding(_endPointBinding, _maxReceivedMessageSize), new EndpointAddress(address));                      
                        channel = Configure(_channelFactory).CreateChannel();
                    }
                    else
                    {
                        _channelFactory = new ChannelFactory<T>();
                        channel = Configure(_channelFactory).CreateChannel(new EndpointAddress(address));
                    }
                }
                else if (!string.IsNullOrEmpty(_defaultServiceLocation))
                {
                    string address = _defaultServiceLocation;
                    if (!address.EndsWith("/"))
                        address += "/";

                    address += _endPointContract.Substring(1) + ".svc";


                    if (!string.IsNullOrEmpty(_endPointBinding))
                    {
                        _channelFactory = new ChannelFactory<T>(_getBinding(_endPointBinding, _maxReceivedMessageSize), new EndpointAddress(address));
                        channel = Configure(_channelFactory).CreateChannel();
                    }
                    else
                    {
                        _channelFactory = new ChannelFactory<T>();
                        channel = Configure(_channelFactory).CreateChannel(new EndpointAddress(address));
                    }
                }
                else
                { 
                    //EndPoint Config case
                    if (string.IsNullOrEmpty(_endPointBinding))
                        _endPointBinding = "WSHttpBinding";

                    _channelFactory = new ChannelFactory<T>(_endPointBinding + _endPointContract);
                    channel = Configure(_channelFactory).CreateChannel();
                }
                if(_operationTimeout > 0)
                    ((IContextChannel)channel).OperationTimeout = new TimeSpan(0, _operationTimeout, 0);
                
                //Create a dynamic proxy on the proxy to inject code to dispose the proxy
                return DynamicProxyFactory.Instance.CreateProxy(channel, new InvocationDelegate(RemoteInvocationHandler));
            }

            /// <summary>
            /// Inject code after remote service method call to dispose the proxy created by the factory
            /// </summary>
            /// <param name="target"></param>
            /// <param name="method"></param>
            /// <param name="parameters"></param>
            /// <returns></returns>
            private static object RemoteInvocationHandler(object target, MethodBase method, object[] parameters)
            {
                object result = null;
                try
                {
                    //SFWK.Core.LogHelper.Log(target, "invoking", method.Name, ELogSeverity.Information );
                    result = method.Invoke(target, parameters);

                }
                catch (TargetInvocationException tiex)
                {
                    //SFWK.Core.LogHelper.Log(target, "RemoteInvocation", tiex, SFWK.Core.ELogSeverity.Error);
                    throw ((System.Exception)(tiex)).InnerException;
                }
                catch (Exception ex)
                {
                    //SFWK.Core.LogHelper.Log(target, "RemoteInvocation", ex, SFWK.Core.ELogSeverity.Error);
                    throw (ex);
                }
                finally
                {
                    //Dispose the proxy after method call
                    if (target is IDisposable) ((IDisposable)target).Dispose();
                }
                return result;
            }

            #endregion

            #region IDisposable Members
            /// <summary>
            /// Dipose the factory
            /// </summary>
            void IDisposable.Dispose()
            {
                if (_channelFactory != null)
                    _channelFactory.Close();
            }

            #endregion
        }
        #endregion

        #region private methods
        /// <summary>
        /// Returns the right Binding object depending on the requested name
        /// WSHttpBinding is return by default 
        /// </summary>
        /// <param name="bindingName"></param>
        /// <returns></returns>
        private static Binding _getBinding(string bindingName, long maxReceivedMessageSize)
        {

            //Enlarge size of strings containned in messages, because serialization of lambda expression give very large strings to pass to search services
            XmlDictionaryReaderQuotas quotas = new XmlDictionaryReaderQuotas();
           // quotas.MaxDepth = 100;
            quotas.MaxStringContentLength = int.MaxValue;
            quotas.MaxArrayLength = int.MaxValue;
            //quotas.MaxBytesPerRead = 4096;
            //quotas.MaxNameTableCharCount = 16384;

            Binding res = null;
            if (!string.IsNullOrEmpty(bindingName) && bindingName.Length > 1)
            {
                EBinding binding = (EBinding)Enum.Parse(typeof(EBinding), bindingName.Trim('_'));
                switch (binding)
                {
                    case EBinding.BasicHttpBinding:
                        {
                            res = new BasicHttpBinding();
                            ((BasicHttpBinding)res).MaxReceivedMessageSize = maxReceivedMessageSize;
                            ((BasicHttpBinding)res).MaxBufferPoolSize = maxReceivedMessageSize;
                            ((BasicHttpBinding)res).MaxBufferSize = int.MaxValue;
                            ((BasicHttpBinding)res).ReaderQuotas = quotas;
                            break;
                        }
                    case EBinding.WSHttpBinding:
                        {
                            res = new WSHttpBinding();
                            ((WSHttpBinding)res).MaxReceivedMessageSize = maxReceivedMessageSize;
                            ((WSHttpBinding)res).MaxBufferPoolSize = maxReceivedMessageSize;
                            ((WSHttpBinding)res).ReaderQuotas = quotas;
                            ((WSHttpBinding)res).TransactionFlow = true;
                            ((WSHttpBinding)res).CloseTimeout = TimeSpan.FromMinutes(20);
                            ((WSHttpBinding)res).OpenTimeout = TimeSpan.FromMinutes(20);
                            ((WSHttpBinding)res).ReceiveTimeout = TimeSpan.FromMinutes(20);
                            ((WSHttpBinding)res).SendTimeout = TimeSpan.FromMinutes(20);
                            break;
                        }
                    case EBinding.WSDualHttpBinding:
                        {
                            res = new WSDualHttpBinding();
                            ((WSDualHttpBinding)res).MaxReceivedMessageSize = maxReceivedMessageSize;
                            ((WSDualHttpBinding)res).MaxBufferPoolSize = maxReceivedMessageSize;
                            ((WSDualHttpBinding)res).ReaderQuotas = quotas;
                            break;
                        }
                    case EBinding.WSFederationHttpBinding:
                        {
                            res = new WSFederationHttpBinding();
                            ((WSFederationHttpBinding)res).MaxReceivedMessageSize = maxReceivedMessageSize;
                            ((WSFederationHttpBinding)res).MaxBufferPoolSize = maxReceivedMessageSize;
                            ((WSFederationHttpBinding)res).ReaderQuotas = quotas;
                            break;
                        }
                    case EBinding.NetTcpBinding:
                        {
                            res = new NetTcpBinding();
                            ((NetTcpBinding)res).MaxReceivedMessageSize = maxReceivedMessageSize;
                            ((NetTcpBinding)res).MaxBufferPoolSize = maxReceivedMessageSize;
                            ((NetTcpBinding)res).ReaderQuotas = quotas;
                            break;
                        }
                    case EBinding.NetPeerTcpBinding:
                        {
                            res = new NetPeerTcpBinding();
                            ((NetPeerTcpBinding)res).MaxReceivedMessageSize = maxReceivedMessageSize;
                            ((NetPeerTcpBinding)res).MaxBufferPoolSize = maxReceivedMessageSize;
                            ((NetPeerTcpBinding)res).ReaderQuotas = quotas;
                            break;
                        }
                    case EBinding.NetNamedPipeBinding:
                        {
                            res = new NetNamedPipeBinding();
                            ((NetNamedPipeBinding)res).MaxReceivedMessageSize = maxReceivedMessageSize;
                            ((NetNamedPipeBinding)res).MaxBufferPoolSize = maxReceivedMessageSize;
                            ((NetNamedPipeBinding)res).ReaderQuotas = quotas;
                            break;
                        }
                    case EBinding.NetMsmqBinding:
                        {
                            res = new NetMsmqBinding();
                            ((NetMsmqBinding)res).MaxReceivedMessageSize = maxReceivedMessageSize;
                            ((NetMsmqBinding)res).MaxBufferPoolSize = maxReceivedMessageSize;
                            ((NetMsmqBinding)res).ReaderQuotas = quotas;
                            break;
                        }
                    case EBinding.MsmqIntegrationBinding:
                        {
                            res = new MsmqIntegrationBinding();
                            ((MsmqIntegrationBinding)res).MaxReceivedMessageSize = maxReceivedMessageSize;
                            break;
                        }
                }
            }
            else
            {
                //By default : wsHttpBinding
                res = new WSHttpBinding();
                ((WSHttpBinding)res).MaxReceivedMessageSize = maxReceivedMessageSize;
                ((WSHttpBinding)res).MaxBufferPoolSize = maxReceivedMessageSize;
                ((WSHttpBinding)res).ReaderQuotas = quotas;
                ((WSHttpBinding)res).TransactionFlow = true;
            }
            return res;
        }


        /// <summary>
        /// Gets the type corresponding to the assembly name and type name passed as parameters
        /// </summary>
        /// <param name="assembly">Assembly name</param>
        /// <param name="type">type name</param>
        /// <returns></returns>
        private static Type _getType(string assembly, string type)
        {
            return System.Reflection.Assembly.Load(assembly.Trim()).GetType(type.Trim());
        }

        /// <summary>
        /// Gets the right assembly and interface name if possible corresponding to the requested type
        /// </summary>
        /// <param name="type"></param>
        /// <param name="assembly"></param>
        /// <param name="interfaceName"></param>
        private static void _getInterfaceInfo(string type, ref Assembly assembly, ref string interfaceName)
        {
            if (type.Contains(","))
            {
                string[] objFrom = type.Split(',');
                interfaceName = objFrom[0].Trim();
                assembly = System.Reflection.Assembly.Load(objFrom[1].Trim());
            }
            else
            {
                assembly = System.Reflection.Assembly.Load(type);
            }
        }

        private static bool _isInterfaceRegistered(string interfaceName)
        {
            if (_registeredTypes == null)
                _registeredTypes = new System.Collections.Hashtable();
            return _registeredTypes.ContainsKey(interfaceName);
        }

        private static void _registerInterface(string interfaceName)
        {
            if (_registeredTypes == null)
                _registeredTypes = new System.Collections.Hashtable();
            _registeredTypes.Add(interfaceName, interfaceName);
        }
        #endregion

        #region Dynamic Proxy Stuff (Local implementation)


        private static void _registerLocalWithDynamicProxy(Type tinterface, Type timplementation)
        {
            _container.AddNewExtension<StaticFactoryExtension>();
            IStaticFactoryConfiguration cff = _container.Configure<IStaticFactoryConfiguration>();

            ICreateDynamicProxy instance = (ICreateDynamicProxy)Activator.CreateInstance(typeof(UnityDynamicProxyFactory), timplementation);

            MethodInfo mig = cff.GetType().GetMethod("RegisterFactory", new Type[] { typeof(FactoryDelegate) });
            MethodInfo mis = mig.MakeGenericMethod(new System.Type[] { tinterface });
            mis.Invoke(cff, new object[] { new FactoryDelegate(instance.CreateProxy) });
        }

        private static object InvocationHandler(object target, MethodBase method, object[] parameters)
        {
            object result = null;
            //_beforeCall(method.Name, parameters);
            try
            {
                result = method.Invoke(target, parameters);
            }
            catch (TargetInvocationException tiex)
            {
                throw ((System.Exception)(tiex)).InnerException;
            }
            catch(Exception ex)
            {
                throw (ex);
            }
            finally
            {
            //    _afterCall(method.Name, parameters);
            }
            return result;
        }
        //private static void _beforeCall(string operationName, object[] parameters)
        //{
        //    Inspector.IInspectBeforeCall inspector = _container.Resolve<Inspector.IInspectBeforeCall>();
        //    if (inspector != null)
        //    {
        //        inspector.Inspect(operationName, parameters);
        //    }
        //}
        //private static void _afterCall(string operationName, object[] parameters)
        //{
        //    Inspector.IInspectAfterCall inspector = _container.Resolve<Inspector.IInspectAfterCall>();
        //    if (inspector != null)
        //    {
        //        inspector.Inspect(operationName, parameters);
        //    }
        //}
        #endregion
    }

    public class ServiceBehavior : IEndpointBehavior
    {

        #region IEndpointBehavior Members

        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
            //throw new NotImplementedException();
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
        {
            clientRuntime.MessageInspectors.Add(new MessageInspector());
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MessageInspector());

        }

        public void Validate(ServiceEndpoint endpoint)
        {
            //throw new NotImplementedException();
        }

        #endregion
    }

    public class MessageInspector : IClientMessageInspector, IDispatchMessageInspector//, IServiceCallContext
    {
        #region IClientMessageInspector Members

        public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
        }

        public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
        {
            var principal = Thread.CurrentPrincipal;

            if (principal.Identity.IsAuthenticated)
            {
                //load roles only if the have been cached because RoleProvider use a webService to load them (circular call problem)
                string[] roles = new string[] { };
                if (principal is RolePrincipal && ((RolePrincipal)principal).IsRoleListCached && !((RolePrincipal)principal).Expired)
                {
                    roles = ((RolePrincipal)principal).GetRoles();
                    var headerValue = CryptographyHelper.Encrypt(principal.Identity.Name + "|" + string.Join(",", roles), SFWKAuthorizationPolicy.HeaderCryptPassword);
                    request.Headers.Add(MessageHeader.CreateHeader(SFWKAuthorizationPolicy.HeaderName, SFWKAuthorizationPolicy.HeaderNamespace, headerValue));
                }
            }
            
            return request;
        }

        #endregion

        #region IDispatchMessageInspector Members

        public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
        {
            return null;
        }

        public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
        }

        #endregion
    }
}

